<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.2">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"eryangz.gitee.io","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":"mac"},"back2top":{"enable":true,"sidebar":false,"scrollpercent":true},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="python元类这篇文章主要讲解元类这个概念。通过学习元类，我对python的面向对象有了更加深入的了解，这里将csnd（https:&#x2F;&#x2F;www.cnblogs.com&#x2F;tkqasn&#x2F;p&#x2F;6524879.html)这篇写的比较好的博客照搬了过来">
<meta property="og:type" content="article">
<meta property="og:title" content="python元类">
<meta property="og:url" content="https://eryangz.gitee.io/20201228/python%E5%85%83%E7%B1%BB/index.html">
<meta property="og:site_name" content="二扬">
<meta property="og:description" content="python元类这篇文章主要讲解元类这个概念。通过学习元类，我对python的面向对象有了更加深入的了解，这里将csnd（https:&#x2F;&#x2F;www.cnblogs.com&#x2F;tkqasn&#x2F;p&#x2F;6524879.html)这篇写的比较好的博客照搬了过来">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-12-28T12:11:56.000Z">
<meta property="article:modified_time" content="2023-05-20T09:05:32.945Z">
<meta property="article:author" content="二扬">
<meta property="article:tag" content="python">
<meta property="article:tag" content="metaclass">
<meta property="article:tag" content="元类">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://eryangz.gitee.io/20201228/python%E5%85%83%E7%B1%BB/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>python元类 | 二扬</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">二扬</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">blog</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-fw fa-home"></i>首页</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-fw fa-user"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-fw fa-tags"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-fw fa-th"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-fw fa-archive"></i>归档</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://eryangz.gitee.io/20201228/python%E5%85%83%E7%B1%BB/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="二扬">
      <meta itemprop="description" content="念念不忘, 必有回响">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="二扬">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          python元类
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2020-12-28 20:11:56" itemprop="dateCreated datePublished" datetime="2020-12-28T20:11:56+08:00">2020-12-28</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2023-05-20 17:05:32" itemprop="dateModified" datetime="2023-05-20T17:05:32+08:00">2023-05-20</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/python/" itemprop="url" rel="index"><span itemprop="name">python</span></a>
                </span>
            </span>

          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="fa fa-file-word-o"></i>
              </span>
                <span class="post-meta-item-text">本文字数：</span>
              <span>16k</span>
            </span>
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="fa fa-clock-o"></i>
              </span>
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              <span>14 分钟</span>
            </span>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h3 id="python元类"><a href="#python元类" class="headerlink" title="python元类"></a>python元类</h3><p>这篇文章主要讲解元类这个概念。通过学习元类，我对python的面向对象有了更加深入的了解，这里将csnd（<a target="_blank" rel="noopener" href="https://www.cnblogs.com/tkqasn/p/6524879.html)%E8%BF%99%E7%AF%87%E5%86%99%E7%9A%84%E6%AF%94%E8%BE%83%E5%A5%BD%E7%9A%84%E5%8D%9A%E5%AE%A2%E7%85%A7%E6%90%AC%E4%BA%86%E8%BF%87%E6%9D%A5">https://www.cnblogs.com/tkqasn/p/6524879.html)这篇写的比较好的博客照搬了过来</a></p>
<span id="more"></span>

<h4 id="一、理解类也是对象"><a href="#一、理解类也是对象" class="headerlink" title="一、理解类也是对象"></a>一、理解类也是对象</h4><p>在理解元类之前，你需要先掌握Python中的类。Python中类的概念借鉴于Smalltalk，这显得有些奇特。在大多数编程语言中，类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">class ObjectCreator(object):</span><br><span class="line">    pass</span><br><span class="line"></span><br><span class="line">my_object = ObjectCreator()</span><br><span class="line">print my_object</span><br><span class="line">#输出：&lt;__main__.ObjectCreator object at 0x8974f2c&gt;</span><br></pre></td></tr></table></figure>

<p>但是，Python中的类还远不止如此。类同样也是一种对象。只要你使用关键字class，Python解释器在执行的时候就会创建一个对象。下面的代码段：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">class ObjectCreator(object):</span><br><span class="line">    pass</span><br></pre></td></tr></table></figure>

<p>将在内存中创建一个对象，名字就是ObjectCreator。这个对象（类）自身拥有创建对象（类实例）的能力，而这就是为什么它是一个类的原因。但是，它的本质仍然是一个对象，于是你可以对它做如下的操作：<br>你可以将它赋值给一个变量， 你可以拷贝它， 你可以为它增加属性， 你可以将它作为函数参数进行传递。<br>下面是示例:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">print ObjectCreator     # 你可以打印一个类，因为它其实也是一个对象</span><br><span class="line">#输出：&lt;class &#x27;__main__.ObjectCreator&#x27;&gt;</span><br><span class="line"></span><br><span class="line">Idef echo(o):</span><br><span class="line">     print o</span><br><span class="line"></span><br><span class="line">echo(ObjectCreator)                 # 你可以将类做为参数传给函数</span><br><span class="line">#输出：&lt;class &#x27;__main__.ObjectCreator&#x27;&gt;</span><br><span class="line">print hasattr(ObjectCreator, &#x27;new_attribute&#x27;)</span><br><span class="line">#输出：False</span><br><span class="line"></span><br><span class="line">ObjectCreator.new_attribute = &#x27;foo&#x27; #  你可以为类增加属性</span><br><span class="line">print hasattr(ObjectCreator, &#x27;new_attribute&#x27;)</span><br><span class="line">#输出：True</span><br><span class="line">print ObjectCreator.new_attribute</span><br><span class="line">#输出：foo</span><br><span class="line"></span><br><span class="line">ObjectCreatorMirror = ObjectCreator # 你可以将类赋值给一个变量</span><br><span class="line">print ObjectCreatorMirror()</span><br><span class="line">#输出：&lt;__main__.ObjectCreator object at 0x108551310&gt;</span><br></pre></td></tr></table></figure>

<h4 id="二、动态地创建类"><a href="#二、动态地创建类" class="headerlink" title="二、动态地创建类"></a>二、动态地创建类</h4><p>1、通过return class动态的构建需要的类</p>
<p>因为类也是对象，你可以在运行时动态的创建它们，就像其他任何对象一样。首先，你可以在函数中创建类，使用class关键字即可。 </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">def choose_class(name):</span><br><span class="line">    if name == &#x27;foo&#x27;:</span><br><span class="line">        class Foo(object):</span><br><span class="line">            pass</span><br><span class="line">        return Foo     # 返回的是类，不是类的实例</span><br><span class="line">    else:</span><br><span class="line">        class Bar(object):</span><br><span class="line">            pass</span><br><span class="line">        return Bar</span><br><span class="line">MyClass = choose_class(&#x27;foo&#x27;)</span><br><span class="line"></span><br><span class="line">print MyClass              # 函数返回的是类，不是类的实例</span><br><span class="line">#输出：&lt;class &#x27;__main__.Foo&#x27;&gt;</span><br><span class="line"></span><br><span class="line">print MyClass()            # 你可以通过这个类创建类实例，也就是对象</span><br><span class="line">#输出：&lt;__main__.Foo object at 0x1085ed950</span><br></pre></td></tr></table></figure>

<p>2、通过type函数构造类</p>
<p>但这还不够动态，因为你仍然需要自己编写整个类的代码。由于类也是对象，所以它们必须是通过什么东西来生成的才对。当你使用class关键字时，Python解释器自动创建这个对象。但就和Python中的大多数事情一样，Python仍然提供给你手动处理的方法。还记得内建函数type吗？这个古老但强大的函数能够让你知道一个对象的类型是什么，就像这样：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">print type(1)</span><br><span class="line">#输出：&lt;type &#x27;int&#x27;&gt;</span><br><span class="line">print type(&quot;1&quot;)</span><br><span class="line">#输出：&lt;type &#x27;str&#x27;&gt;</span><br><span class="line">print type(ObjectCreator)</span><br><span class="line">#输出：&lt;type &#x27;type&#x27;&gt;</span><br><span class="line">print type(ObjectCreator())</span><br><span class="line">#输出：&lt;class &#x27;__main__.ObjectCreator&#x27;&gt;</span><br></pre></td></tr></table></figure>

<p>这里，type有一种完全不同的能力，它也能动态的创建类。type可以接受一个类的描述作为参数，然后返回一个类。（我知道，根据传入参数的不同，同一个函数拥有两种完全不同的用法是一件很傻的事情，但这在Python中是为了保持向后兼容性）<br>type的语法：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">type(类名, 父类的元组（针对继承的情况，可以为空），包含属性的字典（名称和值）)</span><br></pre></td></tr></table></figure>

<p>比如下面的代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">class MyShinyClass(object):</span><br><span class="line">    pass</span><br></pre></td></tr></table></figure>

<p>可以手动通过type创建，其实</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MyShinyClass = type(&#x27;MyShinyClass&#x27;, (), &#123;&#125;)  # 返回一个类对象</span><br><span class="line">print MyShinyClass</span><br><span class="line">#输出：&lt;class &#x27;__main__.MyShinyClass&#x27;&gt;</span><br><span class="line">print MyShinyClass()  #  创建一个该类的实例</span><br><span class="line">#输出：&lt;__main__.MyShinyClass object at 0x1085cd810&gt;</span><br></pre></td></tr></table></figure>

<p>你会发现我们使用“MyShinyClass”作为类名，并且也可以把它当做一个变量来作为类的引用。<br>接下来我们通过一个具体的例子看看type是如何创建类的，范例：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">1、构建Foo类</span><br><span class="line">#构建目标代码</span><br><span class="line">class Foo(object):</span><br><span class="line">    bar = True</span><br><span class="line">#使用type构建</span><br><span class="line">Foo = type(&#x27;Foo&#x27;, (), &#123;&#x27;bar&#x27;:True&#125;)</span><br><span class="line"></span><br><span class="line">2.继承Foo类</span><br><span class="line">#构建目标代码：</span><br><span class="line">class FooChild(Foo):</span><br><span class="line">    pass</span><br><span class="line">#使用type构建</span><br><span class="line">FooChild = type(&#x27;FooChild&#x27;, (Foo,),&#123;&#125;)</span><br><span class="line"></span><br><span class="line">print FooChild</span><br><span class="line">#输出：&lt;class &#x27;__main__.FooChild&#x27;&gt;</span><br><span class="line">print FooChild.bar   # bar属性是由Foo继承而来</span><br><span class="line">#输出：True</span><br><span class="line"></span><br><span class="line">3.为Foochild类增加方法</span><br><span class="line">def echo_bar(self):</span><br><span class="line">    print self.bar</span><br><span class="line"></span><br><span class="line">FooChild = type(&#x27;FooChild&#x27;, (Foo,), &#123;&#x27;echo_bar&#x27;: echo_bar&#125;)</span><br><span class="line">hasattr(Foo, &#x27;echo_bar&#x27;)</span><br><span class="line">#输出：False</span><br><span class="line">hasattr(FooChild, &#x27;echo_bar&#x27;)</span><br><span class="line">#输出：True</span><br><span class="line">my_foo = FooChild()</span><br><span class="line">my_foo.echo_bar()</span><br><span class="line">#输出：True</span><br></pre></td></tr></table></figure>
<p>可以看到，在Python中，类也是对象，你可以动态的创建类。这就是当我们使用关键字class时Python在幕后做的事情，而这就是通过元类来实现的。</p>
<p>三、元类<br>1、什么是元类<br>通过上文的描述，我们知道了Python中的类也是对象。元类就是用来创建这些类（对象）的，元类就是类的类，你可以这样理解为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MyClass = MetaClass()    #元类创建</span><br><span class="line">MyObject = MyClass()     #类创建实例</span><br><span class="line"># 实际上MyClass就是通过type()来创创建出MyClass类，它是type()类的一个实例；同时MyClass本身也是类，也可以创建出自己的实例，这里就是MyObject</span><br></pre></td></tr></table></figure>

<p>函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢？好吧，我猜这是为了和str保持一致性，str是用来创建字符串对象的类，而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查<code>__class__</code>属性来看到这一点。Python中所有的东西，注意，我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象，而且它们都是从一个类创建而来。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">age = 35</span><br><span class="line">age.__class__</span><br><span class="line">#输出：&lt;type &#x27;int&#x27;&gt;</span><br><span class="line">name = &#x27;bob&#x27;</span><br><span class="line">name.__class__</span><br><span class="line">#输出：&lt;type &#x27;str&#x27;&gt;</span><br><span class="line">def foo(): pass</span><br><span class="line">foo.__class__</span><br><span class="line">#输出：&lt;type &#x27;function&#x27;&gt;</span><br><span class="line">class Bar(object): pass</span><br><span class="line">b = Bar()</span><br><span class="line">b.__class__</span><br><span class="line">#输出：&lt;class &#x27;__main__.Bar&#x27;&gt;</span><br><span class="line"></span><br><span class="line">对于任何一个__class__的__class__属性又是什么呢？</span><br><span class="line">a.__class__.__class__</span><br><span class="line">#输出：&lt;type &#x27;type&#x27;&gt;</span><br><span class="line">age.__class__.__class__</span><br><span class="line">#输出：&lt;type &#x27;type&#x27;&gt;</span><br><span class="line">foo.__class__.__class__</span><br><span class="line">#输出：&lt;type &#x27;type&#x27;&gt;</span><br><span class="line">b.__class__.__class__</span><br><span class="line">#输出：&lt;type &#x27;type&#x27;&gt;</span><br></pre></td></tr></table></figure>
<p>因此，元类就是创建类这种对象的东西， type就是Python的内建元类，当然了，你也可以创建自己的元类。</p>
<p>2、<code>__metaclass__</code>属性<br>你可以在写一个类的时候为其添加<code>__metaclass__</code>属性,定义了<code>__metaclass__</code>就定义了这个类的元类。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">class Foo(object):   #py2</span><br><span class="line">    __metaclass__ = something…</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class Foo(metaclass=something):   #py3</span><br><span class="line">    __metaclass__ = something…</span><br></pre></td></tr></table></figure>

<p>例如：当我们写如下代码时 :</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">class Foo(Bar):</span><br><span class="line">    pass</span><br></pre></td></tr></table></figure>

<p>在该类并定义的时候，它还没有在内存中生成，知道它被调用。Python做了如下的操作：<br>1）Foo中有<code>__metaclass__</code>这个属性吗？如果是，Python会在内存中通过<code>__metaclass__</code>创建一个名字为Foo的类对象（我说的是类对象，请紧跟我的思路）。<br>2）如果Python没有找到<code>__metaclass__</code>，它会继续在父类中寻找<code>__metaclass__</code>属性，并尝试做和前面同样的操作。<br>3）如果Python在任何父类中都找不到<code>__metaclass__</code>，它就会在模块层次中去寻找<code>__metaclass__</code>，并尝试做同样的操作。<br>4）如果还是找不到<code>__metaclass__</code>,Python就会用内置的type来创建这个类对象。</p>
<p>现在的问题就是，你可以在<code>__metaclass__</code>中放置些什么代码呢？<br>答案就是：可以创建一个类的东西。那么什么可以用来创建一个类呢？type，或者任何使用到type或者子类化type的东西都可以。</p>
<p>三、自定义元类</p>
<p>元类的主要目的就是为了当创建类时能够自动地改变类。通常，你会为API做这样的事情，你希望可以创建符合当前上下文的类。假想一个很傻的例子，你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到，但其中一种就是通过设定<code>__metaclass__</code>。采用这种方法，这个模块中的所有类都会通过这个元类来创建，我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。</p>
<p><code>__metaclass__</code>实际上可以被任意调用，它并不需要是一个正式的类。所以，我们这里就先以一个简单的函数作为例子开始。</p>
<p>1、使用函数当做元类</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"># 元类会自动将你通常传给‘type’的参数作为自己的参数传入</span><br><span class="line">def upper_attr(future_class_name, future_class_parents, future_class_attr):</span><br><span class="line">    &#x27;&#x27;&#x27;返回一个类对象，将属性都转为大写形式&#x27;&#x27;&#x27;</span><br><span class="line">    #选择所有不以&#x27;__&#x27;开头的属性</span><br><span class="line">    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith(&#x27;__&#x27;))</span><br><span class="line">    # 将它们转为大写形式</span><br><span class="line">    uppercase_attr = dict((name.upper(), value) for name, value in attrs)</span><br><span class="line">    #通过&#x27;type&#x27;来做类对象的创建</span><br><span class="line">    return type(future_class_name, future_class_parents, uppercase_attr)#返回一个类</span><br><span class="line"></span><br><span class="line">class Foo(object):</span><br><span class="line">    __metaclass__ = upper_attr</span><br><span class="line">    bar = &#x27;bip&#x27;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">print hasattr(Foo, &#x27;bar&#x27;)</span><br><span class="line"># 输出: False</span><br><span class="line">print hasattr(Foo, &#x27;BAR&#x27;)</span><br><span class="line"># 输出:True</span><br><span class="line"> </span><br><span class="line">f = Foo()</span><br><span class="line">print f.BAR</span><br><span class="line"># 输出:&#x27;bip&#x27;</span><br></pre></td></tr></table></figure>

<p>2、使用class来当做元类</p>
<p>由于<code>__metaclass__</code>必须返回一个类。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"># 请记住，&#x27;type&#x27;实际上是一个类，就像&#x27;str&#x27;和&#x27;int&#x27;一样。所以，你可以从type继承</span><br><span class="line"># __new__ 是在__init__之前被调用的特殊方法，__new__是用来创建对象并返回之的方法，__new_()是一个类方法</span><br><span class="line"># 而__init__只是用来将传入的参数初始化给对象，它是在对象创建之后执行的方法。</span><br><span class="line"># 你很少用到__new__，除非你希望能够控制对象的创建。这里，创建的对象是类，我们希望能够自定义它，所以我们这里改写__new__</span><br><span class="line"># 如果你希望的话，你也可以在__init__中做些事情。还有一些高级的用法会涉及到改写__call__特殊方法，但是我们这里不用，下面我们可以单独的讨论这个使用</span><br><span class="line"></span><br><span class="line">class UpperAttrMetaClass(type):</span><br><span class="line">    def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):</span><br><span class="line">        attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith(&#x27;__&#x27;))</span><br><span class="line">        uppercase_attr = dict((name.upper(), value) for name, value in attrs)</span><br><span class="line">        return type(future_class_name, future_class_parents, uppercase_attr)#返回一个对象，但同时这个对象是一个类</span><br></pre></td></tr></table></figure>
<p>但是，这种方式其实不是OOP。我们直接调用了type，而且我们没有改写父类的<code>__new__</code>方法。现在让我们这样去处理:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">class UpperAttrMetaclass(type):</span><br><span class="line">    def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr):</span><br><span class="line">        attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith(&#x27;__&#x27;))</span><br><span class="line">        uppercase_attr = dict((name.upper(), value) for name, value in attrs)</span><br><span class="line"> </span><br><span class="line">        # 复用type.__new__方法</span><br><span class="line">        # 这就是基本的OOP编程，没什么魔法。由于type是元类也就是类，因此它本身也是通过__new__方法生成其实例，只不过这个实例是一个类.</span><br><span class="line">        return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)</span><br></pre></td></tr></table></figure>
<p>你可能已经注意到了有个额外的参数upperattr_metaclass，这并没有什么特别的。类方法的第一个参数总是表示当前的实例，就像在普通的类方法中的self参数一样。当然了，为了清晰起见，这里的名字我起的比较长。但是就像self一样，所有的参数都有它们的传统名称。因此，在真实的产品代码中一个元类应该是像这样的：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">class UpperAttrMetaclass(type):</span><br><span class="line">    def __new__(cls, name, bases, dct):</span><br><span class="line">        attrs = ((name, value) for name, value in dct.items() if not name.startswith(&#x27;__&#x27;)</span><br><span class="line">        uppercase_attr  = dict((name.upper(), value) for name, value in attrs)</span><br><span class="line">        return type.__new__(cls, name, bases, uppercase_attr)</span><br></pre></td></tr></table></figure>
<p>如果使用super方法的话，我们还可以使它变得更清晰一些。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">class UpperAttrMetaclass(type):</span><br><span class="line">    def __new__(cls, name, bases, dct):</span><br><span class="line">        attrs = ((name, value) for name, value in dct.items() if not name.startswith(&#x27;__&#x27;))</span><br><span class="line">        uppercase_attr = dict((name.upper(), value) for name, value in attrs)</span><br><span class="line">        return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)</span><br></pre></td></tr></table></figure>

<p>四、使用原来创建ORM的实例<br>我们通过创建一个类似Django中的ORM来熟悉一下元类的使用，通常元类用来创建API是非常好的选择，使用元类的编写很复杂但使用者可以非常简洁的调用API。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">#我们想创建一个类似Django的ORM，只要定义字段就可以实现对数据库表和字段的操作。</span><br><span class="line">class User(Model):</span><br><span class="line">    # 定义类的属性到列的映射：</span><br><span class="line">    id = IntegerField(&#x27;id&#x27;)</span><br><span class="line">    name = StringField(&#x27;username&#x27;)</span><br><span class="line">    email = StringField(&#x27;email&#x27;)</span><br><span class="line">    password = StringField(&#x27;password&#x27;)</span><br><span class="line"></span><br><span class="line"># 例如：</span><br><span class="line"># 创建一个实例：</span><br><span class="line">u = User(id=12345, name=&#x27;Michael&#x27;, email=&#x27;test@orm.org&#x27;, password=&#x27;my-pwd&#x27;)</span><br><span class="line"># 保存到数据库：</span><br><span class="line">u.save()</span><br></pre></td></tr></table></figure>
<p>接下来我么来实现这么个功能：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line">#coding:utf-8</span><br><span class="line">#一、首先来定义Field类，它负责保存数据库表的字段名和字段类型：</span><br><span class="line">class Field(object):</span><br><span class="line">    def __init__(self, name, column_type):</span><br><span class="line">        self.name = name</span><br><span class="line">        self.column_type = column_type</span><br><span class="line">    def __str__(self):</span><br><span class="line">        return &#x27;&lt;%s:%s&gt;&#x27; % (self.__class__.__name__, self.name)</span><br><span class="line"></span><br><span class="line">class StringField(Field):</span><br><span class="line">    def __init__(self, name):</span><br><span class="line">        super(StringField, self).__init__(name, &#x27;varchar(100)&#x27;)</span><br><span class="line"></span><br><span class="line">class IntegerField(Field):</span><br><span class="line">    def __init__(self, name):</span><br><span class="line">        super(IntegerField, self).__init__(name, &#x27;bigint&#x27;)</span><br><span class="line"></span><br><span class="line">#二、定义元类，控制Model对象的创建</span><br><span class="line">class ModelMetaclass(type):</span><br><span class="line">    &#x27;&#x27;&#x27;定义元类&#x27;&#x27;&#x27;</span><br><span class="line">    def __new__(cls, name, bases, attrs):</span><br><span class="line">        if name==&#x27;Model&#x27;:</span><br><span class="line">            return super(ModelMetaclass,cls).__new__(cls, name, bases, attrs)</span><br><span class="line">        mappings = dict()</span><br><span class="line">        for k, v in attrs.iteritems():</span><br><span class="line">            # 保存类属性和列的映射关系到mappings字典</span><br><span class="line">            if isinstance(v, Field):</span><br><span class="line">                print(&#x27;Found mapping: %s==&gt;%s&#x27; % (k, v))</span><br><span class="line">                mappings[k] = v</span><br><span class="line">        for k in mappings.iterkeys():</span><br><span class="line">            #将类属性移除，使定义的类字段不污染User类属性，只在实例中可以访问这些key</span><br><span class="line">            attrs.pop(k)</span><br><span class="line">        attrs[&#x27;__table__&#x27;] = name.lower() # 假设表名和为类名的小写,创建类时添加一个__table__类属性</span><br><span class="line">        attrs[&#x27;__mappings__&#x27;] = mappings # 保存属性和列的映射关系，创建类时添加一个__mappings__类属性</span><br><span class="line">        return super(ModelMetaclass,cls).__new__(cls, name, bases, attrs)</span><br><span class="line"></span><br><span class="line">#三、编写Model基类</span><br><span class="line">class Model(dict):</span><br><span class="line">    __metaclass__ = ModelMetaclass</span><br><span class="line"></span><br><span class="line">    def __init__(self, **kw):</span><br><span class="line">        super(Model, self).__init__(**kw)</span><br><span class="line"></span><br><span class="line">    def __getattr__(self, key):</span><br><span class="line">        try:</span><br><span class="line">            return self[key]</span><br><span class="line">        except KeyError:</span><br><span class="line">            raise AttributeError(r&quot;&#x27;Model&#x27; object has no attribute &#x27;%s&#x27;&quot; % key)</span><br><span class="line"></span><br><span class="line">    def __setattr__(self, key, value):</span><br><span class="line">        self[key] = value</span><br><span class="line"></span><br><span class="line">    def save(self):</span><br><span class="line">        fields = []</span><br><span class="line">        params = []</span><br><span class="line">        args = []</span><br><span class="line">        for k, v in self.__mappings__.iteritems():</span><br><span class="line">            fields.append(v.name)</span><br><span class="line">            params.append(&#x27;?&#x27;)</span><br><span class="line">            args.append(getattr(self, k, None))</span><br><span class="line">        sql = &#x27;insert into %s (%s) values (%s)&#x27; % (self.__table__, &#x27;,&#x27;.join(fields), &#x27;,&#x27;.join(params))</span><br><span class="line">        print(&#x27;SQL: %s&#x27; % sql)</span><br><span class="line">        print(&#x27;ARGS: %s&#x27; % str(args))</span><br><span class="line"></span><br><span class="line">#最后，我们使用定义好的ORM接口，使用起来非常的简单。</span><br><span class="line">class User(Model):</span><br><span class="line">    # 定义类的属性到列的映射：</span><br><span class="line">    id = IntegerField(&#x27;id&#x27;)</span><br><span class="line">    name = StringField(&#x27;username&#x27;)</span><br><span class="line">    email = StringField(&#x27;email&#x27;)</span><br><span class="line">    password = StringField(&#x27;password&#x27;)</span><br><span class="line"></span><br><span class="line"># 创建一个实例：</span><br><span class="line">u = User(id=12345, name=&#x27;Michael&#x27;, email=&#x27;test@orm.org&#x27;, password=&#x27;my-pwd&#x27;)</span><br><span class="line"># 保存到数据库：</span><br><span class="line">u.save()</span><br><span class="line"></span><br><span class="line">#输出</span><br><span class="line"># Found mapping: email==&gt;&lt;StringField:email&gt;</span><br><span class="line"># Found mapping: password==&gt;&lt;StringField:password&gt;</span><br><span class="line"># Found mapping: id==&gt;&lt;IntegerField:id&gt;</span><br><span class="line"># Found mapping: name==&gt;&lt;StringField:username&gt;</span><br><span class="line"># SQL: insert into User (password,email,username,id) values (?,?,?,?)</span><br><span class="line"># ARGS: [&#x27;my-pwd&#x27;, &#x27;test@orm.org&#x27;, &#x27;Michael&#x27;, 12345]</span><br><span class="line"></span><br><span class="line">ORM代码</span><br></pre></td></tr></table></figure>

<p>五、使用<code>__new__</code>方法和元类方式分别实现单例模式<br>1、<code>__new__</code>、<code>__init__</code>、<code>__call__</code>的介绍</p>
<p>在讲到使用元类创建单例模式之前，比需了解<code>__new__</code>这个内置方法的作用，在上面讲元类的时候我们用到了<code>__new__</code>方法来实现类的创建。然而我在那之前还是对<code>__new__</code>这个方法和<code>__init__</code>方法有一定的疑惑。因此这里花点时间对其概念做一次了解和区分。</p>
<ul>
<li><p><code>__new__</code>方法负责创建一个实例对象，在对象被创建的时候调用该方法它是一个类方法。<code>__new__</code>方法在返回一个实例之后，会自动的调用<code>__init__</code>方法，对实例进行初始化。如果<code>__new__</code>方法不返回值，或者返回的不是实例，那么它就不会自动的去调用<code>__init__</code>方法。</p>
</li>
<li><p><code>__init__</code>方法负责将该实例对象进行初始化，在对象被创建之后调用该方法，在<code>__new__</code>方法创建出一个实例后对实例属性进行初始化。<code>__init__</code>方法可以没有返回值。</p>
</li>
<li><p><code>__call__</code>方法其实和类的创建过程和实例化没有多大关系了，定义了<code>__call__</code>方法才能被使用函数的方式执行。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">例如：</span><br><span class="line">class A(object):</span><br><span class="line">    def __call__(self):</span><br><span class="line">        print &quot;__call__ be called&quot;</span><br><span class="line"></span><br><span class="line">a = A()</span><br><span class="line">a()</span><br><span class="line">#输出</span><br><span class="line">#__call__ be called</span><br></pre></td></tr></table></figure></li>
</ul>
<p>打个比方帮助理解：如果将创建实例的过程比作建一个房子。</p>
<ul>
<li>那么class就是一个房屋的设计图，他规定了这个房子有几个房间，每个人房间的大小朝向等。这个设计图就是累的结构</li>
<li><code>__new__</code>就是一个房屋的框架，每个具体的房屋都需要先搭好框架后才能进行专修，当然现有了房屋设计才能有具体的房屋框架出来。这个就是从类到类实例的创建。</li>
<li><code>__init__</code>就是装修房子的过程，对房屋的墙面和地板等颜色材质的丰富就是它该做的事情，当然先有具体的房子框架出来才能进行装饰了。这个就是实例属性的初始化，它是在<code>__new__</code>出一个实例后才能初始化。</li>
<li><code>__call__</code>就是房子的电话，有了固定电话，才能被打电话嘛（就是通过括号的方式像函数一样执行）。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">#coding:utf-8</span><br><span class="line">class Foo(object):</span><br><span class="line">    def __new__(cls, *args, **kwargs):</span><br><span class="line">        #__new__是一个类方法，在对象创建的时候调用</span><br><span class="line">        print &quot;excute __new__&quot;</span><br><span class="line">        return super(Foo,cls).__new__(cls,*args,**kwargs)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    def __init__(self,value):</span><br><span class="line">        #__init__是一个实例方法，在对象创建后调用，对实例属性做初始化</span><br><span class="line">        print &quot;excute __init&quot;</span><br><span class="line">        self.value = value</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">f1 = Foo(1)</span><br><span class="line">print f1.value</span><br><span class="line">f2 = Foo(2)</span><br><span class="line">print f2.value</span><br><span class="line"></span><br><span class="line">#输出===：</span><br><span class="line">excute __new__</span><br><span class="line">excute __init</span><br><span class="line">1</span><br><span class="line">excute __new__</span><br><span class="line">excute __init</span><br><span class="line">2</span><br><span class="line">#====可以看出new方法在init方法之前执行</span><br></pre></td></tr></table></figure>
子类如果重写<code>__new__</code>方法，一般依然要调用父类的<code>__new__</code>方法。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">class Child(Foo):</span><br><span class="line">    def __new__(cls, *args, **kwargs):        </span><br><span class="line">        return suyper(Child, cls).__new__(cls, *args, **kwargs)</span><br></pre></td></tr></table></figure>
必须注意的是，类的<code>__new__</code>方法之后，必须生成本类的实例才能自动调用本类的<code>__init__</code>方法进行初始化，否则不会自动调用<code>__init__</code>.<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">class Foo(object):</span><br><span class="line">    def __init__(self, *args, **kwargs):</span><br><span class="line">        print &quot;Foo __init__&quot;</span><br><span class="line">    def __new__(cls, *args, **kwargs):</span><br><span class="line">        return object.__new__(Stranger, *args, **kwargs)</span><br><span class="line"></span><br><span class="line">class Stranger(object):</span><br><span class="line">    def __init__(self,name):</span><br><span class="line">        print &quot;class Stranger&#x27;s __init__ be called&quot;</span><br><span class="line">        self.name = name</span><br><span class="line"></span><br><span class="line">foo = Foo(&quot;test&quot;)</span><br><span class="line">print type(foo) #&lt;class &#x27;__main__.Stranger&#x27;&gt;</span><br><span class="line">print foo.name #AttributeError: &#x27;Stranger&#x27; object has no attribute &#x27;name&#x27;</span><br><span class="line"></span><br><span class="line">#说明：如果new方法返回的不是本类的实例，那么本类（Foo）的init和生成的类(Stranger)的init都不会被调用</span><br></pre></td></tr></table></figure></li>
</ul>
<p>2.实现单例模式：</p>
<p>依照Python官方文档的说法，<code>__new__</code>方法主要是当你继承一些不可变的class时(比如int, str, tuple)， 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。接下来我们分别通过这两种方式来实现单例模式。当初在看到cookbook中的元类来实现单例模式的时候对其相当疑惑，因此才有了上面这些对元类的总结。</p>
<p>简单来说，单例模式的原理就是通过在类属性中添加一个单例判定位ins_flag，通过这个flag判断是否已经被实例化过了,如果被实例化过了就返回该实例。</p>
<p>1)<code>__new__</code>方法实现单例：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">class Singleton(object):</span><br><span class="line">    def __new__(cls, *args, **kwargs):</span><br><span class="line">        if not hasattr(cls,&quot;_instance&quot;):</span><br><span class="line">            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)</span><br><span class="line">        return cls._instance</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">s1 = Singleton()</span><br><span class="line">s2 = Singleton()</span><br><span class="line"></span><br><span class="line">print s1 is s2</span><br></pre></td></tr></table></figure>
<p>因为重写<code>__new__</code>方法，所以继承至Singleton的类，在不重写<code>__new__</code>的情况下都将是单例模式。</p>
<p>2）元类实现单例</p>
<p>当初我也很疑惑为什么我们是从写使用元类的<code>__init__</code>方法，而不是使用<code>__new__</code>方法来初为元类增加一个属性。其实我只是上面那一段关于元类中<code>__new__</code>方法迷惑了，它主要用于我们需要对类的结构进行改变的时候我们才要重写这个方法。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">class Singleton(type):</span><br><span class="line">    def __init__(self, *args, **kwargs):</span><br><span class="line">        print &quot;__init__&quot;</span><br><span class="line">        self.__instance = None</span><br><span class="line">        super(Singleton,self).__init__(*args, **kwargs)</span><br><span class="line"></span><br><span class="line">    def __call__(self, *args, **kwargs):</span><br><span class="line">        print &quot;__call__&quot;</span><br><span class="line">        if self.__instance is None:</span><br><span class="line">            self.__instance = super(Singleton,self).__call__(*args, **kwargs)</span><br><span class="line">        return self.__instance</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class Foo(object):</span><br><span class="line">    __metaclass__ = Singleton #在代码执行到这里的时候，元类中的__new__方法和__init__方法其实已经被执行了，而不是在Foo实例化的时候执行。且仅会执行一次。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">foo1 = Foo()</span><br><span class="line">foo2 = Foo()</span><br><span class="line">print Foo.__dict__  #_Singleton__instance&#x27;: &lt;__main__.Foo object at 0x100c52f10&gt; 存在一个私有属性来保存属性，而不会污染Foo类（其实还是会污染，只是无法直接通过__instance属性访问）</span><br><span class="line"></span><br><span class="line">print foo1 is foo2  # True</span><br><span class="line"></span><br><span class="line"># 输出</span><br><span class="line"># __init__</span><br><span class="line"># __call__</span><br><span class="line"># __call__</span><br><span class="line"># &#123;&#x27;__module__&#x27;: &#x27;__main__&#x27;, &#x27;__metaclass__&#x27;: &lt;class &#x27;__main__.Singleton&#x27;&gt;, &#x27;_Singleton__instance&#x27;: &lt;__main__.Foo object at 0x100c52f10&gt;, &#x27;__dict__&#x27;: &lt;attribute &#x27;__dict__&#x27; of &#x27;Foo&#x27; objects&gt;, &#x27;__weakref__&#x27;: &lt;attribute &#x27;__weakref__&#x27; of &#x27;Foo&#x27; objects&gt;, &#x27;__doc__&#x27;: None&#125;</span><br><span class="line"># True</span><br></pre></td></tr></table></figure>
<p>基于这个例子：</p>
<ul>
<li>我们知道元类(Singleton)生成的实例是一个类(Foo),而这里我们仅仅需要对这个实例(Foo)增加一个属性(<code>__instance</code>)来判断和保存生成的单例。想想也知道为一个类添加一个属性当然是在<code>__init__</code>中实现了。</li>
<li>关于<code>__call__</code>方法的调用，因为Foo是Singleton的一个实例。所以Foo()这样的方式就调用了Singleton的<code>__call__</code>方法。不明白就回头看看上一节中的<code>__call__</code>方法介绍。<br>假如我们通过元类的<code>__new__</code>方法来也可以实现，但显然没有通过<code>__init__</code>来实现优雅，因为我们不会为了为实例增加一个属性而重写<code>__new__</code>方法。所以这个形式不推荐。<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">class Singleton(type):</span><br><span class="line">    def __new__(cls, name,bases,attrs):</span><br><span class="line">        print &quot;__new__&quot;</span><br><span class="line">        attrs[&quot;_instance&quot;] = None</span><br><span class="line">        return  super(Singleton,cls).__new__(cls,name,bases,attrs)</span><br><span class="line"></span><br><span class="line">    def __call__(self, *args, **kwargs):</span><br><span class="line">        print &quot;__call__&quot;</span><br><span class="line">        if self._instance is None:</span><br><span class="line">            self._instance = super(Singleton,self).__call__(*args, **kwargs)</span><br><span class="line">        return self._instance</span><br><span class="line"></span><br><span class="line">class Foo(object):</span><br><span class="line">    __metaclass__ = Singleton</span><br><span class="line"></span><br><span class="line">foo1 = Foo()</span><br><span class="line">foo2 = Foo()</span><br><span class="line">print Foo.__dict__ </span><br><span class="line">print foo1 is foo2  # True</span><br><span class="line"></span><br><span class="line"># 输出</span><br><span class="line"># __new__</span><br><span class="line"># __call__</span><br><span class="line"># __call__</span><br><span class="line"># &#123;&#x27;__module__&#x27;: &#x27;__main__&#x27;, &#x27;__metaclass__&#x27;: &lt;class &#x27;__main__.Singleton&#x27;&gt;, &#x27;_instance&#x27;: &lt;__main__.Foo object at 0x103e07ed0&gt;, &#x27;__dict__&#x27;: &lt;attribute &#x27;__dict__&#x27; of &#x27;Foo&#x27; objects&gt;, &#x27;__weakref__&#x27;: &lt;attribute &#x27;__weakref__&#x27; of &#x27;Foo&#x27; objects&gt;, &#x27;__doc__&#x27;: None&#125;</span><br><span class="line"># True</span><br></pre></td></tr></table></figure></li>
</ul>

    </div>

    
    
    
        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>二扬
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="https://eryangz.gitee.io/20201228/python%E5%85%83%E7%B1%BB/" title="python元类">https://eryangz.gitee.io/20201228/python元类/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fa fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


      <footer class="post-footer">
          
          <div class="post-tags">
              <a href="/tags/python/" rel="tag"><i class="fa fa-tag"></i> python</a>
              <a href="/tags/metaclass/" rel="tag"><i class="fa fa-tag"></i> metaclass</a>
              <a href="/tags/%E5%85%83%E7%B1%BB/" rel="tag"><i class="fa fa-tag"></i> 元类</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/20200718/pgsql%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8/" rel="prev" title="pgsql常用命令使用">
      <i class="fa fa-chevron-left"></i> pgsql常用命令使用
    </a></div>
      <div class="post-nav-item">
    <a href="/20210303/ubuntu%E9%83%A8%E7%BD%B2odoo14/" rel="next" title="ubuntu部署odoo14">
      ubuntu部署odoo14 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#python%E5%85%83%E7%B1%BB"><span class="nav-text">python元类</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E7%90%86%E8%A7%A3%E7%B1%BB%E4%B9%9F%E6%98%AF%E5%AF%B9%E8%B1%A1"><span class="nav-text">一、理解类也是对象</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E4%BA%8C%E3%80%81%E5%8A%A8%E6%80%81%E5%9C%B0%E5%88%9B%E5%BB%BA%E7%B1%BB"><span class="nav-text">二、动态地创建类</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="二扬"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">二扬</p>
  <div class="site-description" itemprop="description">念念不忘, 必有回响</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">26</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">27</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/EryangZzz" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;EryangZzz" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://gitee.com/" title="Gitee → https:&#x2F;&#x2F;gitee.com&#x2F;" rel="noopener" target="_blank"><i class="fa fa-fw fa-github-alt"></i>Gitee</a>
      </span>
      <span class="links-of-author-item">
        <a href="/1289256052" title="QQ → 1289256052"><i class="fa fa-fw fa-qq"></i>QQ</a>
      </span>
      <span class="links-of-author-item">
        <a href="/1289256052@qq.com" title="E-Mail → 1289256052@qq.com"><i class="fa fa-fw fa-envelope"></i>E-Mail</a>
      </span>
  </div>



<script type="text/javascript" charset="utf-8" src="/js/tagcloud.js"></script>
<script type="text/javascript" charset="utf-8" src="/js/tagcanvas.js"></script>
<div class="widget-wrap">
    <iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=110 src="//music.163.com/outchain/player?type=0&id=8435349308&auto=1&height=66"></iframe>
    <h3 class="widget-title">标签云</h3>
    <div id="myCanvasContainer" class="widget tagcloud">
        <canvas width="250" height="250" id="resCanvas" style="width=100%">
            <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/emqx/" rel="tag">emqx</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/flask/" rel="tag">flask</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/" rel="tag">git</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/js/" rel="tag">js</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/metaclass/" rel="tag">metaclass</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mqtt/" rel="tag">mqtt</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/" rel="tag">nginx</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/odoo/" rel="tag">odoo</a><span class="tag-list-count">10</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/odoo%E5%89%8D%E7%AB%AF/" rel="tag">odoo前端</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/odoo%E9%83%A8%E7%BD%B2/" rel="tag">odoo部署</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/office%E9%A2%84%E8%A7%88/" rel="tag">office预览</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/pgsql/" rel="tag">pgsql</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/pip%E6%BA%90/" rel="tag">pip源</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/pyinstaller/" rel="tag">pyinstaller</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/python/" rel="tag">python</a><span class="tag-list-count">9</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/re/" rel="tag">re</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/rtsp/" rel="tag">rtsp</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/time%E6%A8%A1%E5%9D%97/" rel="tag">time模块</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ubuntu/" rel="tag">ubuntu</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ubuntu%E6%BA%90/" rel="tag">ubuntu源</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%85%83%E7%B1%BB/" rel="tag">元类</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" rel="tag">多线程</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%9C%8D%E5%8A%A1%E9%85%8D%E7%BD%AE/" rel="tag">服务配置</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/" rel="tag">正则表达式</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%AC%91%E8%84%B8%E4%BA%A4%E4%BA%92/" rel="tag">笑脸交互</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%A7%86%E9%A2%91%E6%B5%81/" rel="tag">视频流</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" rel="tag">设计模式</a><span class="tag-list-count">1</span></li></ul>
        </canvas>
    </div>
</div>




      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">二扬</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
      <span class="post-meta-item-text">站点总字数：</span>
    <span title="站点总字数">105k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span class="post-meta-item-text">站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">1:35</span>
</div>



        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  
  <script>
    (function(){
      var bp = document.createElement('script');
      var curProtocol = window.location.protocol.split(':')[0];
      bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(bp, s);
    })();
  </script>
<script>
(function(){
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
}
else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
 var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
</script>





  
<script src="/js/local-search.js"></script>













  

  

</body>
</html>
